﻿using System;
using System.IO.Ports;

namespace IchiaTester.Modbus
{
    /*
     *  Example :
         // 外部创建和配置 SerialPort
        SerialPort serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);

        // 创建 ModbusRtu 对象，传入外部创建的 SerialPort,打开串口
        ModbusRTU modbusRTU = new ModbusRTU(serialPort);
        modbusRTU.Open();
     */

    public class ModbusRtu
    {
        private SerialPort serialPort;

        /// <summary>
        /// 单参构造
        /// </summary>
        /// <param name="serialPort">串口对象</param>
        public ModbusRtu(SerialPort serialPort)
        {
            this.serialPort = serialPort;
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        public void Open()
        {
            if (!serialPort.IsOpen)
            {
                serialPort.Open();
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public void Close()
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
        }

        /// <summary>
        /// 读取保持型寄存器
        /// </summary>
        /// <param name="slaveAddress">从站地址</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="numberOfRegisters">读取的寄存器数量</param>
        /// <param name="responseDataLength">相应数据长度</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public byte[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfRegisters,
            int responseDataLength)
        {
            // 清除输入缓冲区,以避免读取到旧数据
            serialPort.DiscardInBuffer();

            // 请求帧结构: 地址(1字节) 功能码(1字节) 起始地址(2字节) 寄存器数(2字节) CRC(2字节)
            byte[] frame = new byte[8];
            frame[0] = slaveAddress;
            frame[1] = 0x03; // 功能码 0x03 (读取保持寄存器)
            frame[2] = (byte)(startAddress >> 8);
            frame[3] = (byte)startAddress;
            frame[4] = (byte)(numberOfRegisters >> 8);
            frame[5] = (byte)numberOfRegisters;

            // 计算CRC
            ushort crc = CalculateCRC(frame, 6);
            frame[6] = (byte)(crc & 0xFF);
            frame[7] = (byte)(crc >> 8);

            // 发送请求
            serialPort.Write(frame, 0, frame.Length);

            // 接收响应
            byte[] response = new byte[responseDataLength];
            int bytesRead = serialPort.Read(response, 0, response.Length);

            // 简单的错误处理
            if (bytesRead != response.Length)
                throw new Exception("Incomplete response.");

            // 返回响应数据
            return response;
        }

        /// <summary>
        /// 计算CRC
        /// </summary>
        /// <param name="data"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public ushort CalculateCRC(byte[] data, int length)
        {
            ushort crc = 0xFFFF;
            for (int pos = 0; pos < length; pos++)
            {
                crc ^= (ushort)data[pos]; // XOR byte into least sig. byte of crc

                for (int i = 8; i != 0; i--) // Loop over each bit
                {
                    if ((crc & 0x0001) != 0) // If the LSB is set
                    {
                        crc >>= 1; // Shift right and XOR 0xA001
                        crc ^= 0xA001;
                    }
                    else // Else LSB is not set
                        crc >>= 1; // Just shift right
                }
            }

            return crc;
        }
    }

    public class ModbusTcp
    {
        // ModbusTcp 待实现 todo
    }

    // 其他可能的輔助類或工具類
}